﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SimpleRest.Core.Extensions.Documentation;
using SimpleRest.Core.Extensions.OAuth.Exceptions;

namespace SimpleRest.Core.Extensions.OAuth
{
    /// <summary>
    /// also referred to as the Authorization Server in other workflows.  This service takes an authorization code or user credentials and creates
    /// an access token.
    /// http://tools.ietf.org/html/rfc6749#section-5.2
    /// </summary>
    public abstract class AccessTokenServiceBase : AccessTokenServiceBase<AccessToken>
    {
    } 

    /// <summary>
    /// Also referred to as the Authorization Server in other workflows.  This service takes an authorization code or user credentials and creates
    /// an access token.
    /// http://tools.ietf.org/html/rfc6749#section-5.2
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [GeneralDescription("Endpoint allowing oAuth 2.0 access tokens to be created.")]
    [LinkDescription("http://tools.ietf.org/html/rfc6749", Description = "oAuth RFC 6749, issued October 2012.")]
    public abstract class AccessTokenServiceBase<T> : ServiceBase where T : AccessToken, new()
    {
        [GeneralDescription("Create an oAuth 2.0 access token.  Requests made to this endpoint should always be done 'server to server' and never 'client to server'.  Upon successfully requesting an access token the access token issued should be secured and never visible to client's.  A successful response should yield an oAuth 2.0 compliant access token.")]
        [ParameterDescription("grant_type", typeof(string), false, Description = "Located in the content body, this field specifies the type of access token request grant type which can be one of the following: authorization_code, refresh_token, password, or client_credentials.  Each of these grant types are described in the oAuth 2.0 RFC documentation.")]
        [ParameterDescription("code", typeof(string), true, Description = "Located in the content body, this field is optional in all cases except for when the grant_type parameter is set to authorization_code.  This value should contain a valid and non-expired authorization code obtained by this system.")]
        [ParameterDescription("redirect_uri", typeof(string), true, Description = "Located in the content body, this field is optional in all cases except for when the grant_type parameter is set to authorization_code.  This value should contain the redirect_uri value provided in the authorization code request made to this system.")]
        [ParameterDescription("client_id", typeof(string), true, Description = "Located in the content body, this field is optional in all cases except for when the grant_type parameter is set to authorization_code.  This value should contain the client_id parameter given to the consumer by this system.")]
        [ParameterDescription("username", typeof(string), true, Description = "Located in the content body, this field is optional in all cases except for when the grant_type parameter is set to password.  This value should contain the resource owner username given to the consumer by this system.")]
        [ParameterDescription("password", typeof(string), true, Description = "Located in the content body, this field is optional in all cases except for when the grant_type parameter is set to password.  This value should contain the resource owner password given to the consumer by this system.")]
        [ParameterDescription("scope", typeof(string[]), true, Description = "Located in the content body, this field is optional in all cases and only honored when the grant_type parameter is set to either client_credentials, password, or refresh_token.  This value should contain a list of space-delimited, case-sensitive strings.  The strings are defined by the authorization server owned by this system.")]
        [ParameterDescription("refresh_token", typeof(string), true, Description = "Located in the content body, this field is optional in all cases and only honored when the grant_type parameter is set to refresh_token.  This value should contain the refresh token issued to the client on a previous successful access token request.")]
        [ParameterDescription("client_secret", typeof(string), true, Description = "Located in the content body, this field is optional in all cases and only honored when the grant_type parameter is set to refresh_token.  This value should contain the client secret issued to them by this system.")]
        [LinkDescription("http://tools.ietf.org/html/rfc6749#section-4.1.3", Description = "Access Token Request: authorization_code")]
        [LinkDescription("http://tools.ietf.org/html/rfc6749#section-4.3.2", Description = "Access Token Request: password")]
        [LinkDescription("http://tools.ietf.org/html/rfc6749#section-4.4.2", Description = "Access Token Request: client_credentials")]
        [LinkDescription("http://tools.ietf.org/html/rfc6749#section-6", Description = "Access Token Request: refresh_token")]
        [LinkDescription("http://tools.ietf.org/html/rfc6749#section-3.3", Description = "Access Token Scope")]
        [StatusCodeDescription(ErrorCode = "unsupported_grant_type", StatusCode = System.Net.HttpStatusCode.BadRequest, Description = "Returned when a grant_type was received and is not supported.")]
        [StatusCodeDescription(ErrorCode = "invalid_scope", StatusCode = System.Net.HttpStatusCode.BadRequest, Description = "Returned when a scope value was received and is not supported.")]
        [StatusCodeDescription(ErrorCode = "access_denied", StatusCode = System.Net.HttpStatusCode.BadRequest, Description = "Returned when the request is denied.")]
        [StatusCodeDescription(ErrorCode = "unauthorized", StatusCode = System.Net.HttpStatusCode.Unauthorized, Description = "Returned when the request is not authorized.")]
        [WebPost(UriTemplate = "")]
        public T CreateToken([HttpBody]AccessTokenRequest request)
        {
            if (string.IsNullOrEmpty(request.grant_type))
                throw new UnsupportedGrantTypeException();

            if (request.grant_type.ToLower() == "authorization_code")
            {
                return this.CreateAccessToken(request.code, request.redirect_uri, request.client_id);
            }
            else if (request.grant_type.ToLower() == "refresh_token")
            {
                return this.ReIssueAccessToken(request.refresh_token, request.client_id, request.client_secret, request.scope);
            }
            else if (request.grant_type.ToLower() == "password")
            {
                return this.CreateAccessToken(new Credentials() { Username = request.username, Password = request.password, Scope = request.scope });
            }
            else if (request.grant_type.ToLower() == "client_credentials")
            {
                return this.GrantAnonymousAccessToken();
            }

            throw new UnsupportedGrantTypeException();
        }

        /// <summary>
        /// Create an access token from an authorization code obtained from consumer previously.
        /// </summary>
        /// <param name="authorizationCode"></param>
        /// <param name="redirectUri"></param>
        /// <param name="clientId"></param>
        /// <returns></returns>
        public virtual T CreateAccessToken(string authorizationCode, string redirectUri, string clientId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Create an access token from user credentials passed in.
        /// </summary>
        /// <param name="credentials"></param>
        /// <returns></returns>
        public virtual T CreateAccessToken(Credentials credentials)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Reissue access token based on refresh token information.
        /// </summary>
        /// <param name="refreshToken"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        public virtual T ReIssueAccessToken(string refreshToken, string clientId, string clientSecret, string scope = null)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Invoked with grant_type = client_credentials.  This is an anonymous access request for an access token.
        /// </summary>
        /// <returns></returns>
        public virtual T GrantAnonymousAccessToken()
        {
            throw new NotImplementedException();
        }
    }
}
